using SDRSharp.Radio;
using System;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Windows.Forms;

namespace SDRSharp.RTLTCP
{
	public unsafe class RtlTcpIO : IFrontendController, IIQStreamController, ITunableSource, IFloatingConfigDialogProvider, IDisposable
	{
		private const int DongleInfoLength = 12;

		private const string DefaultHost = "192.168.2.1";

		private const short DefaultPort = 8002;

		private const uint DefaultFrequency = 100000000u;

		//private static readonly int _bufferSize;
		private readonly static int _bufferSize = Utils.GetIntSetting("RTLTcpBufferLength", 32 * 1024);

		private const byte CMD_SET_FREQ = 1;

		private const byte CMD_SET_SAMPLE_RATE = 2;

		private const byte CMD_SET_TUNER_GAIN_MODE = 3;

		private const byte CMD_SET_GAIN = 4;

		private const byte CMD_SET_FREQ_COR = 5;

		private const byte CMD_SET_AGC_MODE = 8;

		private const byte CMD_SET_TUNER_GAIN_INDEX = 13;

		private unsafe readonly float* _lutPtr;

		private static readonly UnsafeBuffer _lutBuffer = UnsafeBuffer.Create(4096, sizeof(float));

		private long _frequency = 100000000L;

		private double _sampleRate;

		private string _host;

		private int _port;

		private bool _useRtlAGC;

		private bool _useTunerAGC;

		private uint _tunerGainIndex;

		private uint _tunerGainCount;

		private string _tunerName;

		private uint _tunerType;

		private int _frequencyCorrection;

		private SamplesAvailableDelegate _callback;

		private Thread _sampleThread;

		private UnsafeBuffer _iqBuffer;

		private unsafe Complex* _iqBufferPtr;

		private Socket _s;

		private readonly byte[] _cmdBuffer = new byte[5];

		private readonly RTLTcpSettings _gui;

		public bool IsStreaming
		{
			get
			{
				return this._sampleThread != null;
			}
		}

		public bool IsSoundCardBased
		{
			get
			{
				return false;
			}
		}

		public string SoundCardHint
		{
			get
			{
				return string.Empty;
			}
		}

		public RtlSdrTunerType TunerType
		{
			get
			{
				return (RtlSdrTunerType)this._tunerType;
			}
		}

		public string TunerName
		{
			get
			{
				return (string)this._tunerName;
			}
		}

		public double Samplerate
		{
			get
			{
				return this._sampleRate;
			}
			set
			{
				this._sampleRate = value;
				this.SendCommand(2, (uint)this._sampleRate);
			}
		}

		public bool CanTune
		{
			get
			{
				return true;
			}
		}

		public long MinimumTunableFrequency
		{
			get
			{
				return 0L;
			}
		}

		public long MaximumTunableFrequency
		{
			get
			{
				return 2000000000L;
			}
		}

		public long Frequency
		{
			get
			{
				return this._frequency;
			}
			set
			{
				this._frequency = value;
				this.SendCommand(1, (uint)this._frequency);
			}
		}

		public int FrequencyCorrection
		{
			get
			{
				return this._frequencyCorrection;
			}
			set
			{
				this._frequencyCorrection = value;
				this.SendCommand(5, this._frequencyCorrection);
			}
		}

		public bool UseRtlAGC
		{
			get
			{
				return this._useRtlAGC;
			}
			set
			{
				this._useRtlAGC = value;
				this.SendCommand(8, this._useRtlAGC ? 1 : 0);
			}
		}

		public bool UseTunerAGC
		{
			get
			{
				return this._useTunerAGC;
			}
			set
			{
				this._useTunerAGC = value;
				this.SendCommand(3, this._useTunerAGC ? 0 : 1);
			}
		}

		public uint TunerGainIndex
		{
			get
			{
				return this._tunerGainIndex;
			}
			set
			{
				this._tunerGainIndex = value;
				this.SendCommand(13, (int)this._tunerGainIndex);
			}
		}

		public uint TunerGainCount
		{
			get
			{
				return this._tunerGainCount;
			}
		}

		public void ShowSettingGUI(IWin32Window parent)
		{
			this._gui.Show();
		}

		public void HideSettingGUI()
		{
			this._gui.Hide();
		}
		 
		static RtlTcpIO()
        {
	  
        }

		public RtlTcpIO()
		{
			this._gui = new RTLTcpSettings(this);
			this._gui.Hostname = Utils.GetStringSetting("RTLTcpHost", "192.168.2.1");
			this._gui.Port = Utils.GetIntSetting("RTLTcpPort", 8002);
			this._frequency = 100000000L;
			 
			//_lutBuffer = UnsafeBuffer.Create(4096, sizeof(float));
			_lutPtr = (float*)_lutBuffer;

            const float scale = 1.0f / 2047.5f;
            for (var i = 0; i < 4096; i++)
            {
                _lutPtr[i] = (i - 2047.5f) * scale;
            }
		}

		~RtlTcpIO()
		{
			this.Dispose();
		}

		public void Dispose()
		{
			if (this._gui != null)
			{
				this._gui.Dispose();
			}
			GC.SuppressFinalize(this);
		}

		public void Open()
		{
		}

		public void Close()
		{
			if (this._s != null)
			{
				this._s.Close();
				this._s = null;
			}
		}

		public void Start(SamplesAvailableDelegate callback)
		{
			this._callback = callback;
			this._host = this._gui.Hostname;
			this._port = this._gui.Port;
			this._s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			this._s.NoDelay = true;
			this._s.Connect(this._host, this._port);
			byte[] buffer = new byte[12];
			if (this._s.Receive(buffer, 0, 12, SocketFlags.None) > 0)
			{
				this.ParseDongleInfo(buffer);
			}
			this.SendCommand(2, (uint)this._sampleRate);
			this.SendCommand(5, this._frequencyCorrection);
			this.SendCommand(1, (uint)this._frequency);
			this.SendCommand(8, this._useRtlAGC ? 1u : 0u);
			this.SendCommand(3, this._useTunerAGC ? 0u : 1u);
			this.SendCommand(13, this._tunerGainIndex);
			this._sampleThread = new Thread(new ThreadStart(this.RecieveSamples));
			this._sampleThread.Start();
			Utils.SaveSetting("RTLTcpHost", this._host);
			Utils.SaveSetting("RTLTcpPort", this._port);
		}

		public void Stop()
		{
			this.Close();
			if (this._sampleThread != null)
			{
				this._sampleThread.Join();
				this._sampleThread = null;
			}
			this._callback = null;
		}


		string getName(byte[] buffer)
        {
			byte[]  bytes=new byte[buffer.Length -2];
			Array.Copy(buffer, 2, bytes,0, bytes.Length);
			string str = System.Text.Encoding.UTF8.GetString(bytes);

			return str;
		}

		private void ParseDongleInfo(byte[] buffer)
		{
			if (buffer.Length < 12)
			{
				return;
			}

			this._tunerType = buffer[0];
			this._tunerGainCount = buffer[1];
			this._tunerName = getName(buffer);

			//if (buffer[0] != 'R' || buffer[1] != 'T' || buffer[2] != 'L' || buffer[3] != '0')
			//{
			//	this._tunerType = 0u;
			//	this._tunerGainCount = 0u;
			//	return;
			//}
			//this._tunerType = (uint)((int)buffer[4] << 24 | (int)buffer[5] << 16 | (int)buffer[6] << 8 | (int)buffer[7]);
			//this._tunerGainCount = (uint)((int)buffer[8] << 24 | (int)buffer[9] << 16 | (int)buffer[10] << 8 | (int)buffer[11]);


		}

		public bool SendCommand(byte cmd, byte[] val)
		{
			if (this._s == null || val.Length < 4)
			{
				return false;
			}
			this._cmdBuffer[0] = cmd;
			this._cmdBuffer[1] = val[3];
			this._cmdBuffer[2] = val[2];
			this._cmdBuffer[3] = val[1];
			this._cmdBuffer[4] = val[0];
			try
			{
				this._s.Send(this._cmdBuffer);
			}
			catch
			{
				return false;
			}
			return true;
		}

		private void SendCommand(byte cmd, uint val)
		{
			byte[] bytes = BitConverter.GetBytes(val);
			this.SendCommand(cmd, bytes);
		}

		private void SendCommand(byte cmd, int val)
		{
			byte[] bytes = BitConverter.GetBytes(val);
			this.SendCommand(cmd, bytes);
		}

		private unsafe void RecieveSamples()
		{
			byte[] array = new byte[RtlTcpIO._bufferSize];
			UnsafeBuffer unsafeBuffer = UnsafeBuffer.Create(array);
			byte* ptr = (byte*)unsafeBuffer;
			this._iqBuffer = UnsafeBuffer.Create(RtlTcpIO._bufferSize / 2, sizeof(Complex));
			this._iqBufferPtr = (Complex*)this._iqBuffer;
			int num = 0;
			while (this._s != null && this._s.Connected)
			{
				try
				{
					int num2 = this._s.Receive(array, num, RtlTcpIO._bufferSize - num, SocketFlags.None);
					int num3 = num + num2;
					num = num3 % 2;
					this.ProcessSamples(ptr, num3 - num);
					if (num == 1)
					{
						*ptr = ptr[num3 - 1];
					}
				}
				catch
				{
					this.Close();
					break;
				}
			}
			this._iqBuffer.Dispose();
			this._iqBuffer = null;
			this._iqBufferPtr = null;
			GC.KeepAlive(unsafeBuffer);
		}
        void LogText(byte* rawPtr, Int32 lenth)
        {
            string stream = null;
            if (File.Exists("log.csv"))
            {
                StreamReader reader = new StreamReader("log.csv");
                stream = reader.ReadToEnd();
                reader.Close();
            }

            FileStream fs = new FileStream("log.csv", FileMode.OpenOrCreate);
            StreamWriter sw = new StreamWriter(fs);
           // sw.WriteLine(stream); 

            Int16* ptru16 = (Int16*)rawPtr;

            for (var i = 0; i < lenth; i+=2)
            {
                string cmd = ptru16[i].ToString() + "," + ptru16[i+1].ToString();

                sw.WriteLine(cmd);
            }
             
            //sw.WriteLine(DateTime.Now); 

            sw.Close();
            fs.Close();
        }
		
		 void LogText(Complex* _iqBufferPtr, Int32 lenth)
        {
            string stream = null;
            if (File.Exists("1log.csv"))
            {
                StreamReader reader = new StreamReader("1log.csv");
                stream = reader.ReadToEnd();
                reader.Close();
            }

            FileStream fs = new FileStream("1log.csv", FileMode.OpenOrCreate);
            StreamWriter sw = new StreamWriter(fs);
            // sw.WriteLine(stream); 

           
            var ptr = _iqBufferPtr;

            for (var i = 0; i < lenth; i ++)
            {
                string cmd = ptr[i].Real.ToString() + "," + ptr[i].Imag.ToString();

                sw.WriteLine(cmd);
            }

            //sw.WriteLine(DateTime.Now); 

            sw.Close();
            fs.Close();
        }

        Int16 ii = 0;


   private void ProcessSamples(byte* rawPtr, int len)
        {
            var sampleCount = len / 4;

            if(ii == 1)
            {
                ii = 0;
                LogText(rawPtr, sampleCount);
            } 

            Int16* ptru16 = (Int16 *)rawPtr;

            var ptr = _iqBufferPtr;
            for (var i = 0; i < sampleCount; i++)
            {
				ptr->Real = _lutPtr[(*ptru16++) + 2048];
				ptr->Imag = _lutPtr[(*ptru16++) + 2048];   
				//ptr->Real = (float)((*ptru16++) * 4.8851978505129457743038593063019e-4);
				//ptr->Imag = (float)((*ptru16++) * 4.8851978505129457743038593063019e-4); 
                ptr++;
            }

            if (ii == 1)
            {
                ii = 0;
                LogText(_iqBufferPtr, sampleCount / 2);
            }
             
            if (_callback != null)
            {
                _callback(this, _iqBufferPtr, sampleCount);
            }
        }
		
		private unsafe void ProcessSamples11(byte* rawPtr, int len)
		{
			int num = len / 2;
			Complex* ptr = this._iqBufferPtr;
			for (int i = 0; i < num; i++)
			{
				//ptr->Real = RtlTcpIO._lutPtr[(IntPtr)(*(rawPtr++)) * 4];
				//ptr->Imag = RtlTcpIO._lutPtr[(IntPtr)(*(rawPtr++)) * 4];
				ptr++;
			}
			if (this._callback != null)
			{
				this._callback.Invoke(this, this._iqBufferPtr, num);
			}
		}
	}
}
